#!/usr/bin/env python3
"""
S3 Bucket Lifecycle Configuration Tool - Lifecycle API 演示工具

这个工具演示如何管理 S3 桶的生命周期配置。

使用示例:
    python s3_bucket_lifecycle.py --bucket my-bucket --action get
    python s3_bucket_lifecycle.py --bucket my-bucket --action put --simple
    python s3_bucket_lifecycle.py --bucket my-bucket --action delete
    python s3_bucket_lifecycle.py --format json
    python s3_bucket_lifecycle.py --verbose
"""

import argparse
import json
import sys
import xml.etree.ElementTree as ET
import boto3
from botocore.config import Config
from botocore.exceptions import ClientError, NoCredentialsError
from datetime import datetime, timezone


class BucketLifecycleClient:
    """S3 桶生命周期配置客户端"""
    
    def __init__(self, endpoint_url='http://localhost:8000', 
                 access_key='AKIAIOSFODNN7EXAMPLE', 
                 secret_key='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
                 region='us-east-1'):
        """
        初始化桶生命周期配置客户端
        
        Args:
            endpoint_url: S3 服务端点
            access_key: AWS Access Key
            secret_key: AWS Secret Key
            region: AWS 区域
        """
        self.endpoint_url = endpoint_url
        self.access_key = access_key
        self.secret_key = secret_key
        self.region = region
        self._client = None
    
    def _get_client(self):
        """获取 S3 客户端"""
        if self._client is None:
            self._client = boto3.client(
                's3',
                endpoint_url=self.endpoint_url,
                aws_access_key_id=self.access_key,
                aws_secret_access_key=self.secret_key,
                region_name=self.region,
                config=Config(s3={'addressing_style': 'path'})
            )
        return self._client


    def _create_lifecycle_xml(self, rules):
        """创建生命周期配置 XML"""
        root = ET.Element("LifecycleConfiguration")
        
        for rule in rules:
            rule_elem = ET.SubElement(root, "Rule")
            
            # Required fields
            ET.SubElement(rule_elem, "ID").text = rule.get("id", "DefaultRule")
            ET.SubElement(rule_elem, "Status").text = rule.get("status", "Enabled")
            
            # Filter (optional)
            if "prefix" in rule:
                filter_elem = ET.SubElement(rule_elem, "Filter")
                ET.SubElement(filter_elem, "Prefix").text = rule["prefix"]
            
            # Expiration (optional)
            if "expiration_days" in rule:
                exp_elem = ET.SubElement(rule_elem, "Expiration")
                ET.SubElement(exp_elem, "Days").text = str(rule["expiration_days"])
            
            # Non-current version expiration (optional)
            if "noncurrent_expiration_days" in rule:
                noncurrent_exp = ET.SubElement(rule_elem, "NoncurrentVersionExpiration")
                ET.SubElement(noncurrent_exp, "NoncurrentDays").text = str(rule["noncurrent_expiration_days"])
            
            # Transitions (optional)
            if "transitions" in rule:
                for transition in rule["transitions"]:
                    trans_elem = ET.SubElement(rule_elem, "Transition")
                    ET.SubElement(trans_elem, "Days").text = str(transition["days"])
                    ET.SubElement(trans_elem, "StorageClass").text = transition["storage_class"]
            
            # Non-current version transitions (optional)
            if "noncurrent_transitions" in rule:
                for transition in rule["noncurrent_transitions"]:
                    trans_elem = ET.SubElement(rule_elem, "NoncurrentVersionTransition")
                    ET.SubElement(trans_elem, "NoncurrentDays").text = str(transition["days"])
                    ET.SubElement(trans_elem, "StorageClass").text = transition["storage_class"]
        
        return ET.tostring(root, encoding='unicode')


    def _convert_rules_to_boto3_format(self, rules):
        """将规则转换为 boto3 格式"""
        boto3_rules = []
        for rule in rules:
            boto3_rule = {
                'ID': rule.get('id', 'DefaultRule'),
                'Status': rule.get('status', 'Enabled')
            }
            
            # Filter
            if 'prefix' in rule and rule['prefix']:
                boto3_rule['Filter'] = {'Prefix': rule['prefix']}
            
            # Expiration
            if 'expiration_days' in rule:
                boto3_rule['Expiration'] = {'Days': rule['expiration_days']}
            
            # Non-current version expiration
            if 'noncurrent_expiration_days' in rule:
                boto3_rule['NoncurrentVersionExpiration'] = {'NoncurrentDays': rule['noncurrent_expiration_days']}
            
            # Transitions
            if 'transitions' in rule:
                boto3_rule['Transitions'] = []
                for transition in rule['transitions']:
                    boto3_rule['Transitions'].append({
                        'Days': transition['days'],
                        'StorageClass': transition['storage_class']
                    })
            
            # Non-current version transitions
            if 'noncurrent_transitions' in rule:
                boto3_rule['NoncurrentVersionTransitions'] = []
                for transition in rule['noncurrent_transitions']:
                    boto3_rule['NoncurrentVersionTransitions'].append({
                        'NoncurrentDays': transition['days'],
                        'StorageClass': transition['storage_class']
                    })
            
            boto3_rules.append(boto3_rule)
        
        return boto3_rules

    def put_lifecycle(self, bucket_name, rules):
        """简单格式设置桶生命周期配置"""
        try:
            client = self._get_client()
            xml_config = self._create_lifecycle_xml(rules)
            boto3_rules = self._convert_rules_to_boto3_format(rules)
            
            print(f"🔧 设置桶生命周期配置: {bucket_name}")
            print(f"📋 配置规则数量: {len(rules)}")
            
            client.put_bucket_lifecycle_configuration(
                Bucket=bucket_name,
                LifecycleConfiguration={
                    'Rules': boto3_rules
                }
            )
            
            print("✅ 生命周期配置设置成功")
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            error_msg = e.response['Error']['Message']
            print(f"❌ 设置失败 [{error_code}]: {error_msg}")
            return False
        except NoCredentialsError:
            print("❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"❌ 未知错误: {e}")
            return False
    
    def put_lifecycle_verbose(self, bucket_name, rules):
        """详细格式设置桶生命周期配置"""
        try:
            client = self._get_client()
            xml_config = self._create_lifecycle_xml(rules)
            boto3_rules = self._convert_rules_to_boto3_format(rules)
            
            print("🔧 桶生命周期配置详细信息:")
            print("=" * 60)
            print(f"   🗂️  桶名称: {bucket_name}")
            print(f"   🌐 区域: {self.region}")
            print(f"   🔗 服务端点: {self.endpoint_url}")
            print(f"   📅 操作时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            print(f"   📋 配置规则数量: {len(rules)}")
            print()
            
            # 显示规则详情
            for i, rule in enumerate(rules, 1):
                print(f"   📝 规则 {i}:")
                print(f"      • ID: {rule.get('id', 'DefaultRule')}")
                print(f"      • 状态: {rule.get('status', 'Enabled')}")
                if 'prefix' in rule:
                    print(f"      • 前缀: {rule['prefix']}")
                if 'expiration_days' in rule:
                    print(f"      • 过期天数: {rule['expiration_days']}")
                if 'noncurrent_expiration_days' in rule:
                    print(f"      • 非当前版本过期天数: {rule['noncurrent_expiration_days']}")
                print()
            
            print(f"   📄 XML 配置:")
            print(f"   {xml_config}")
            print()
            
            client.put_bucket_lifecycle_configuration(
                Bucket=bucket_name,
                LifecycleConfiguration={
                    'Rules': boto3_rules
                }
            )
            
            print(f"   ✅ 生命周期配置设置成功")
            print(f"   📊 操作完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            error_msg = e.response['Error']['Message']
            print(f"   ❌ 设置失败:")
            print(f"      错误代码: {error_code}")
            print(f"      错误信息: {error_msg}")
            return False
        except NoCredentialsError:
            print("   ❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"   ❌ 未知错误: {e}")
            return False
    
    def put_lifecycle_json(self, bucket_name, rules):
        """JSON 格式设置桶生命周期配置"""
        try:
            client = self._get_client()
            xml_config = self._create_lifecycle_xml(rules)
            boto3_rules = self._convert_rules_to_boto3_format(rules)
            
            client.put_bucket_lifecycle_configuration(
                Bucket=bucket_name,
                LifecycleConfiguration={
                    'Rules': boto3_rules
                }
            )
            
            result = {
                "success": True,
                "bucket_name": bucket_name,
                "operation": "put_lifecycle",
                "rules_count": len(rules),
                "rules": rules,
                "xml_config": xml_config,
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            error_msg = e.response['Error']['Message']
            result = {
                "success": False,
                "bucket_name": bucket_name,
                "operation": "put_lifecycle",
                "error": error_code,
                "error_message": error_msg,
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except NoCredentialsError:
            result = {
                "success": False,
                "error": "NoCredentials",
                "error_message": "未找到 AWS 凭据",
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except Exception as e:
            result = {
                "success": False,
                "bucket_name": bucket_name,
                "operation": "put_lifecycle",
                "error": "Unknown",
                "error_message": str(e),
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False


    def get_lifecycle(self, bucket_name):
        """简单格式获取桶生命周期配置"""
        try:
            client = self._get_client()
            
            print(f"📋 获取桶生命周期配置: {bucket_name}")
            
            response = client.get_bucket_lifecycle_configuration(Bucket=bucket_name)
            
            print("✅ 生命周期配置:")
            print(json.dumps(response, indent=2, ensure_ascii=False))
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            if error_code == 'NoSuchLifecycleConfiguration':
                print("ℹ️  未找到生命周期配置")
                return True
            else:
                error_msg = e.response['Error']['Message']
                print(f"❌ 获取失败 [{error_code}]: {error_msg}")
                return False
        except NoCredentialsError:
            print("❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"❌ 未知错误: {e}")
            return False
    
    def get_lifecycle_verbose(self, bucket_name):
        """详细格式获取桶生命周期配置"""
        try:
            client = self._get_client()
            
            print("📋 桶生命周期配置详细信息:")
            print("=" * 60)
            print(f"   🗂️  桶名称: {bucket_name}")
            print(f"   🌐 区域: {self.region}")
            print(f"   🔗 服务端点: {self.endpoint_url}")
            print(f"   📅 查询时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            print()
            
            response = client.get_bucket_lifecycle_configuration(Bucket=bucket_name)
            
            print(f"   ✅ 生命周期配置:")
            print(f"   {json.dumps(response, indent=4, ensure_ascii=False)}")
            print(f"   📊 查询完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            if error_code == 'NoSuchLifecycleConfiguration':
                print(f"   ℹ️  未找到生命周期配置")
                print(f"   📊 查询完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
                return True
            else:
                error_msg = e.response['Error']['Message']
                print(f"   ❌ 获取失败:")
                print(f"      错误代码: {error_code}")
                print(f"      错误信息: {error_msg}")
                return False
        except NoCredentialsError:
            print("   ❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"   ❌ 未知错误: {e}")
            return False
    
    def get_lifecycle_json(self, bucket_name):
        """JSON 格式获取桶生命周期配置"""
        try:
            client = self._get_client()
            
            response = client.get_bucket_lifecycle_configuration(Bucket=bucket_name)
            
            result = {
                "success": True,
                "bucket_name": bucket_name,
                "operation": "get_lifecycle",
                "has_configuration": True,
                "lifecycle_configuration": response,
                "query_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            if error_code == 'NoSuchLifecycleConfiguration':
                result = {
                    "success": True,
                    "bucket_name": bucket_name,
                    "operation": "get_lifecycle",
                    "has_configuration": False,
                    "message": "未找到生命周期配置",
                    "query_time": datetime.now().isoformat()
                }
                print(json.dumps(result, indent=2, ensure_ascii=False))
                return True
            else:
                error_msg = e.response['Error']['Message']
                result = {
                    "success": False,
                    "bucket_name": bucket_name,
                    "operation": "get_lifecycle",
                    "error": error_code,
                    "error_message": error_msg,
                    "query_time": datetime.now().isoformat()
                }
                print(json.dumps(result, indent=2, ensure_ascii=False))
                return False
        except NoCredentialsError:
            result = {
                "success": False,
                "error": "NoCredentials",
                "error_message": "未找到 AWS 凭据",
                "query_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except Exception as e:
            result = {
                "success": False,
                "bucket_name": bucket_name,
                "operation": "get_lifecycle",
                "error": "Unknown",
                "error_message": str(e),
                "query_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False


    def delete_lifecycle(self, bucket_name):
        """简单格式删除桶生命周期配置"""
        try:
            client = self._get_client()
            
            print(f"🗑️  删除桶生命周期配置: {bucket_name}")
            
            client.delete_bucket_lifecycle(Bucket=bucket_name)
            
            print("✅ 生命周期配置删除成功")
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            error_msg = e.response['Error']['Message']
            print(f"❌ 删除失败 [{error_code}]: {error_msg}")
            return False
        except NoCredentialsError:
            print("❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"❌ 未知错误: {e}")
            return False
    
    def delete_lifecycle_verbose(self, bucket_name):
        """详细格式删除桶生命周期配置"""
        try:
            client = self._get_client()
            
            print("🗑️  桶生命周期配置删除详细信息:")
            print("=" * 60)
            print(f"   🗂️  桶名称: {bucket_name}")
            print(f"   🌐 区域: {self.region}")
            print(f"   🔗 服务端点: {self.endpoint_url}")
            print(f"   📅 操作时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            print()
            
            client.delete_bucket_lifecycle(Bucket=bucket_name)
            
            print(f"   ✅ 生命周期配置删除成功")
            print(f"   📊 操作完成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            error_msg = e.response['Error']['Message']
            print(f"   ❌ 删除失败:")
            print(f"      错误代码: {error_code}")
            print(f"      错误信息: {error_msg}")
            return False
        except NoCredentialsError:
            print("   ❌ 错误: 未找到 AWS 凭据")
            return False
        except Exception as e:
            print(f"   ❌ 未知错误: {e}")
            return False
    
    def delete_lifecycle_json(self, bucket_name):
        """JSON 格式删除桶生命周期配置"""
        try:
            client = self._get_client()
            
            client.delete_bucket_lifecycle(Bucket=bucket_name)
            
            result = {
                "success": True,
                "bucket_name": bucket_name,
                "operation": "delete_lifecycle",
                "message": "生命周期配置删除成功",
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return True
                
        except ClientError as e:
            error_code = e.response['Error']['Code']
            error_msg = e.response['Error']['Message']
            result = {
                "success": False,
                "bucket_name": bucket_name,
                "operation": "delete_lifecycle",
                "error": error_code,
                "error_message": error_msg,
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except NoCredentialsError:
            result = {
                "success": False,
                "error": "NoCredentials",
                "error_message": "未找到 AWS 凭据",
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False
        except Exception as e:
            result = {
                "success": False,
                "bucket_name": bucket_name,
                "operation": "delete_lifecycle",
                "error": "Unknown",
                "error_message": str(e),
                "operation_time": datetime.now().isoformat()
            }
            print(json.dumps(result, indent=2, ensure_ascii=False))
            return False


def parse_args():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(
        description='S3 桶生命周期配置管理工具',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  %(prog)s --bucket my-bucket --action get              # 获取生命周期配置
  %(prog)s --bucket my-bucket --action put --simple     # 设置简单默认配置
  %(prog)s --bucket my-bucket --action delete           # 删除生命周期配置
  %(prog)s --format json                                # JSON 格式输出
  %(prog)s --verbose                                    # 详细格式输出
  %(prog)s --access-key KEY --secret-key SECRET         # 指定凭据
        """
    )
    
    parser.add_argument(
        '--bucket', '-b',
        required=True,
        help='桶名称'
    )
    
    parser.add_argument(
        '--host',
        default='http://localhost:8000',
        help='S3 服务端点 (默认: http://localhost:8000)'
    )
    
    parser.add_argument(
        '--access-key',
        default='AKIAIOSFODNN7EXAMPLE',
        help='AWS Access Key (默认: 测试密钥)'
    )
    
    parser.add_argument(
        '--secret-key',
        default='wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
        help='AWS Secret Key (默认: 测试密钥)'
    )
    
    parser.add_argument(
        '--region',
        default='us-east-1',
        help='AWS 区域 (默认: us-east-1)'
    )
    
    parser.add_argument(
        '--format',
        choices=['simple', 'verbose', 'json'],
        default='simple',
        help='输出格式 (默认: simple)'
    )
    
    parser.add_argument(
        '--verbose',
        action='store_true',
        help='详细输出模式 (等同于 --format verbose)'
    )
    
    # 操作选项
    parser.add_argument(
        '--action',
        choices=['get', 'put', 'delete'],
        required=True,
        help='要执行的操作'
    )
    
    # Put 操作选项
    parser.add_argument(
        '--config',
        help='JSON 配置文件路径'
    )
    
    parser.add_argument(
        '--simple',
        action='store_true',
        help='使用简单默认配置'
    )
    
    return parser.parse_args()


def main():
    """主函数"""
    args = parse_args()
    
    # 如果指定了 --verbose，覆盖 format 设置
    if args.verbose:
        args.format = 'verbose'
    
    # 创建客户端
    client = BucketLifecycleClient(
        endpoint_url=args.host,
        access_key=args.access_key,
        secret_key=args.secret_key,
        region=args.region
    )
    
    # 根据格式执行相应操作
    success = False
    
    if args.action == "get":
        if args.format == 'simple':
            success = client.get_lifecycle_simple(args.bucket)
        elif args.format == 'verbose':
            success = client.get_lifecycle_verbose(args.bucket)
        elif args.format == 'json':
            success = client.get_lifecycle_json(args.bucket)
    
    elif args.action == "put":
        if args.simple:
            # 简单默认配置
            rules = [{
                "id": "DefaultRule",
                "status": "Enabled",
                "prefix": "",
                "noncurrent_expiration_days": 30,
                "noncurrent_transitions": [{
                    "days": 7,
                    "storage_class": "STANDARD_IA"
                }]
            }]
        elif args.config:
            try:
                with open(args.config, 'r') as f:
                    config = json.load(f)
                    rules = config.get("rules", [])
            except Exception as e:
                print(f"❌ 读取配置文件失败: {e}")
                sys.exit(1)
        else:
            print("❌ 请指定 --config 或 --simple 选项")
            sys.exit(1)
        
        if args.format == 'simple':
            success = client.put_lifecycle_simple(args.bucket, rules)
        elif args.format == 'verbose':
            success = client.put_lifecycle_verbose(args.bucket, rules)
        elif args.format == 'json':
            success = client.put_lifecycle_json(args.bucket, rules)
    
    elif args.action == "delete":
        if args.format == 'simple':
            success = client.delete_lifecycle_simple(args.bucket)
        elif args.format == 'verbose':
            success = client.delete_lifecycle_verbose(args.bucket)
        elif args.format == 'json':
            success = client.delete_lifecycle_json(args.bucket)
    
    # 设置退出码
    sys.exit(0 if success else 1)


if __name__ == "__main__":
    main()
